package com.yunji.common.utils;

import com.yunji.common.exception.GlobalException;
import com.yunji.common.utils.bean.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import static com.yunji.common.constant.Constants.STR_NULL;

/**
 * @Project:    yunji-test-mall
 * @Package:    com.yunji.core.utils
 * @Title:      RandomUtils
 * @Description:
 * 随机工具类
 *
 * History:
 * Date                     Version     Author          Summary
 * ============================================================
 * 2024-03-11 17:59:59      V1.0        HuaAo       新建类
 */

public class RandomUtils
{
    private static final Logger log = LoggerFactory.getLogger(RandomUtils.class);

    private static Resource resource;

    private static synchronized Resource getInstance()
    {
        if (resource == null) resource = new Resource();
        return resource;
    }

    public static String nextCode()
    {
        return RandomUtils.getInstance().next();
    }

    public static boolean checkCode(String code)
    {
        return RandomUtils.getInstance().check(code);
    }

    /**
     * @Title	Resource
     * @Desc	处理随机生成代码 不支持分布式应用
     *          支持同一毫秒生成 2^13=8096个不重复Code。
     * @Date	2024-03-12 09:56:50
     * @Author	HuaAo
     */
    static class Resource
    {
        // 元数据 32位
        private static final String META_M = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
        // 元数据 12位
        private static final String META_D = "0123456789UV";

        private long lastTimestamp = -1L;
        // 2位的序列号
        private long sequence;

        private static final long SEQUENCE_BITS = 13L;

        private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS); // 序列号最大值

        private static final int OFFSET = 1;

        /**
         * @Title	randomCode
         * @Desc	生成随机码
         *          年份-2000 使用2位META_M标识
         *          月份 12   使用1位META_D标识
         *          最多31天  使用1位META_M标识
         *          24小时    使用两位标识前一位0标识0-12 1标识13-24
         *          分秒毫秒使用原数字
         *          再生成2位随机码
         *          再加1位校验码
         *          共16位
         * @Date	2024-03-11 19:42:49
         * @Author	HuaAo
         * @return	String 16位随机码
         */
        private String randomCode()
        {
            // 获取当前时间的年份、月份、日期、时分秒
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(new Date(lastTimestamp));
            int y = calendar.get(Calendar.YEAR);
            Assert.isTrue((y > 0), "生成随机码失败!");
            int month = calendar.get(Calendar.MONTH);
            int d = calendar.get(Calendar.DAY_OF_MONTH);
            int h = calendar.get(Calendar.HOUR_OF_DAY);
            int m = calendar.get(Calendar.MINUTE);
            int s = calendar.get(Calendar.SECOND);
            int ms = calendar.get(Calendar.MILLISECOND);
            int stayY = y % 1000;
            int firstY = stayY / 32;
            int secondY = stayY % 32;
            StringBuilder build = new StringBuilder();
            build.append(META_M.charAt(firstY + OFFSET));
            build.append(META_M.charAt(secondY + OFFSET));
            build.append(META_D.charAt(month));
            build.append(META_M.charAt(d + OFFSET));
            build.append(META_M.charAt((h + 10) - OFFSET));
            build.append(String.valueOf((m + 100)).substring(1));
            build.append(String.valueOf((s + 100)).substring(1));
            build.append(String.valueOf((ms + 1000)).substring(1));
            build.append(String.valueOf((sequence + 1000)).substring(1));
            int checkCode = (int) ((stayY + month + d + h + m + s + ms + sequence + OFFSET) % 32);
            build.append(META_M.charAt(checkCode));
            return build.toString();
        }

        /**
         * @Title	nextCode
         * @Desc	下一个ID生成算法
         * @Date	2023-11-20 10:25:39
         * @Author	HuaAo
         * @return	long 生成唯一ID
         */
        public synchronized String next()
        {
            long timestamp = currentTimeMillis();
            // 获取当前时间戳如果小于上次时间戳，则表示时间戳获取出现异常
            if (timestamp < lastTimestamp)
            {
                log.error("clock is moving backwards. Rejecting requests until 【{}】", lastTimestamp);
                throw new GlobalException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
            }

            // 获取当前时间戳如果等于上次时间戳(同一毫秒内),则在序列号加一;否则序列号赋值为0,从0开始。
            if (lastTimestamp == timestamp)
            {
                sequence = (sequence + 1) & SEQUENCE_MASK;
                if (sequence == 0) { timestamp = nextTimeMillis(lastTimestamp); }
            }
            else
            {
                sequence = 0;
            }

            // 将上次时间戳值刷新
            lastTimestamp = timestamp;

            /*
             * 返回结果：
             * (timestamp - ANCHOR_TIME) << TIMESTAMP_OFFSET) 表示将时间戳减去初始时间戳，再左移相应位数
             * (centerId << CENTER_OFFSET) 表示将数据id左移相应位数
             * (workerId << WORKER_OFFSET) 表示将工作id左移相应位数
             * | 是按位或运算符，例如：x | y，只有当x,y都为0的时候结果才为0,其它情况结果都为1。
             * 因为个部分只有相应位上的值有意义,其它位上都是0,所以将各部分的值进行 | 运算就能得到最终拼接好的id
             */
            return randomCode();
        }

        /**
         * @Title	check
         * @Desc	校验生成代码
         * @Date	2024-03-12 11:38:29
         * @Author	HuaAo
         * @Param	code 生成代码
         * @return	boolean 校验一致true 校验失败false
         */
        public boolean check(String code)
        {
            if (code == null || code.length() != 16) { return false; }
            char firstY = code.charAt(0);
            char secondY = code.charAt(1);
            int firstIndex = META_M.indexOf(firstY);
            int secondIndex = META_M.indexOf(secondY);
            int sum = (firstIndex - OFFSET) * 32 + (secondIndex - OFFSET);
            char month = code.charAt(2);
            int index = META_D.indexOf(month);
            sum += index;
            char d = code.charAt(3);
            index = META_M.indexOf(d) - OFFSET;
            sum += index;
            char h = code.charAt(4);
            index = META_M.indexOf(h) + OFFSET - 10;
            sum += index;
            int m = Integer.parseInt(code.substring(5, 7));
            sum += m;
            int s = Integer.parseInt(code.substring(7, 9));
            sum += s;
            int ms = Integer.parseInt(code.substring(9, 12));
            sum += ms;
            int ordinal = Integer.parseInt(code.substring(12, 15));
            sum += ordinal;
            int checkCode = (sum + OFFSET) % 32;
            char c1 = META_M.charAt(checkCode);
            char c2 = code.charAt(15);
            return c1 == c2;
        }

        /**
         * @Desc	获取下次时间戳
         * @Date	2023-11-20 10:02:00
         * @Author	HuaAo
         * @param	lastTimestamp 最后一次时间戳
         * @return	获取唯一时间戳
         */
        private long nextTimeMillis(long lastTimestamp)
        {
            long timestamp = currentTimeMillis();
            while (timestamp <= lastTimestamp)
            {
                timestamp = currentTimeMillis();
            }
            return timestamp;
        }

        /**
         * @Desc	获取系统时间戳
         * @Date	2023-11-20 10:02:27
         * @Author	HuaAo
         * @return	当前系统时间戳
         */
        private long currentTimeMillis()
        {
            return System.currentTimeMillis();
        }
    }

    /**
     * 用于随机选的数字
     */
    public static final String BASE_NUMBER = "0123456789";
    /**
     * 用于随机选的字符
     */
    public static final String BASE_CHAR = "abcdefghijklmnopqrstuvwxyz";
    /**
     * 用于随机选的字符和数字
     */
    public static final String BASE_CHAR_NUMBER = BASE_CHAR + BASE_NUMBER;

    /**
     * 获得一个随机的字符串（只包含数字和字符）
     *
     * @param length 字符串的长度
     * @return 随机字符串
     */
    public static String randomString(int length)
    {
        return randomString(BASE_CHAR_NUMBER, length);
    }

    /**
     * 获得一个随机的字符串
     *
     * @param baseString 随机字符选取的样本
     * @param length     字符串的长度
     * @return 随机字符串
     */
    public static String randomString(String baseString, int length)
    {
        if (BeanUtils.isNull(baseString)) { return STR_NULL; }

        final StringBuilder build = new StringBuilder(length);

        if (length < 1)
        {
            length = 1;
        }

        int baseLength = baseString.length();
        for (int i = 0; i < length; i++)
        {
            int number = randomInt(baseLength);
            build.append(baseString.charAt(number));
        }
        return build.toString();
    }

    /**
     * 获得指定范围内的随机数 [0,limit)
     *
     * @param limit 限制随机数的范围，不包括这个数
     * @return 随机数
     */
    public static int randomInt(int limit)
    {
        return getRandom().nextInt(limit);
    }

    /**
     * 获取随机数生成器对象<br>
     * ThreadLocalRandom是JDK 7之后提供并发产生随机数，能够解决多个线程发生的竞争争夺。
     *
     * @return {@link ThreadLocalRandom}
     * @since 3.1.2
     */
    public static ThreadLocalRandom getRandom()
    {
        return ThreadLocalRandom.current();
    }

    /**
     * @Title   getRandomMath
     * @Desc    生成随机指定长度数字
     * @Date    2024-10-21 16:31:01.798
     * @param	num 指定长度
     * @Return	String 随机数字
     */
    public static String getRandomMath(Integer num){
        String str="0123456789";
        StringBuilder sb=new StringBuilder(num);
        for(int i=0;i<num;i++)
        {
            char ch=str.charAt(new Random().nextInt(str.length()));
            sb.append(ch);
        }
        return  sb.toString();
    }
}
